﻿/*********************************************************************
 *
 *            J1939 Main Source Code
 *
 *********************************************************************
 *
 *    本程序是由XieTongXueFlyMe对现有的J1939协议文档分析，和对前辈的贡献总结,
 * 写出的一套开源的J1939驱动。
 *    本协议特点：
 *        1.易移植（不针对特定的CAN硬件，只要满足CAN2.0B即可）
 *        2.轻量级（可适应低端的MCU）
 *        3.支持多任务调用接口（可用于嵌入式系统）
 *        4.双模式（轮询或者中断，逻辑更加简单明了）
 *        5.不掉帧（数据采用收发列队缓存）
 *
 *  源代码下载：
 *        https://github.com/XeiTongXueFlyMe/J1939
 *  源代码临时手册Web站点：
 *        https://xeitongxueflyme.github.io/j1939doc.github.io/
 *
 * Version     Date        Description
 * -------------------------------------------------------------------
 * v1.0.0     2017/06/04    首个版本 Version 1 测试版发布
 * v1.0.1     2017/08/04    完善功能
 * v1.1.0     2017/11/22    Version 1 稳定发布版
 * v2.0.1     2017/11/24    Version 2 测试版发布
 * v2.0.2     2018/01/03    解决V2.0.1 遗留问题
 * v2.1.0     2018/01/20    Version 2 稳定发布版
 * Author               Date         changes
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *XeiTongXueFlyMe       7/06/04      首个版本
 *XeiTongXueFlyMe       7/08/04      增加对TP的支持
 *XeiTongXueFlyMe       7/11/24      增加对多路CAN硬件的收发，和报文处理
 *XeiTongXueFlyMe       7/11/29      增加请求和响应API
 *XeiTongXueFlyMe       7/12/07      重做TP接受API函数
 *XeiTongXueFlyMe       7/12/08      增加软件滤波器
 *XeiTongXueFlyMe       8/01/03      重做接受发送API，简化协议栈初始化调用逻辑
 **********************************************************************/
#ifndef __J1939_H
#define __J1939_H
/******************************类型声明*********************************/
#ifndef FALSE
    #define FALSE 0
#endif

#ifndef TRUE
    #define TRUE 1
#endif

/** 统一类型定义
 *  不同的单片机的编译器， int,short,long 的位数可能不同
 *
 *  在移植J1939协议栈时，首先应该配置这里
 */
typedef unsigned int j1939_uint32_t;   /** < 32位无符号整形*/
typedef int j1939_int32_t;             /** < 32位整形*/
typedef unsigned short j1939_uint16_t; /** < 16位无符号整形*/
typedef unsigned char j1939_uint8_t;   /** < 8位无符号整形*/
typedef char j1939_int8_t;             /** < 8位无符号整形*/
typedef int CAN_NODE;                  /* MLC Modified */

#define J1939_NULL 0

//函数返回代码
#define RC_SUCCESS 0        /**< 成功*/
#define RC_QUEUEEMPTY 1     /**< 列队为空*/
#define RC_QUEUEFULL 1      /**< 列队满*/
#define RC_CANNOTRECEIVE 2  /**< 不能接收*/
#define RC_CANNOTTRANSMIT 2 /**< 不能传输*/
#define RC_PARAMERROR 3     /**< 参数错误*/

//内部常量
#define J1939_FALSE 0 /**< 代表函数错误返回*/
#define J1939_TRUE 1  /**< 代表函数正确返回*/

// J1939 默认的优先级（参考J1939文档）
#define J1939_CONTROL_PRIORITY 0x03     /**< J1939文档默认的优先级*/
#define J1939_INFO_PRIORITY 0x06        /**< J1939文档默认的优先级*/
#define J1939_PROPRIETARY_PRIORITY 0x06 /**< J1939文档默认的优先级*/
#define J1939_REQUEST_PRIORITY 0x06     /**< J1939文档默认的优先级*/
#define J1939_ACK_PRIORITY 0x06         /**< J1939文档默认的优先级*/
#define J1939_TP_CM_PRIORITY 0x07       /**< J1939文档默认的优先级*/
#define J1939_TP_DT_PRIORITY 0x07       /**< J1939文档默认的优先级*/

// J1939 定义的地址
#define J1939_GLOBAL_ADDRESS 255 /**< 全局地址*/
#define J1939_NULL_ADDRESS 254   /**< 空地址*/

//J1939协议栈的PNG请求响应，相关的定义
#define J1939_PF_REQUEST2 201 /**< J1939协议栈的请求 PF */
#define J1939_PF_TRANSFER 202 /**< J1939协议栈的转移 PF */

#define J1939_PF_REQUEST 234        /**< 请求 或 用于握手机制*/
#define J1939_PF_ACKNOWLEDGMENT 232 /**< 确认请求 或 用于握手机制*/

#define J1939_ACK_CONTROL_BYTE 0            /**< 用于TP(长帧数据)，代表确认*/
#define J1939_NACK_CONTROL_BYTE 1           /**< 用于TP(长帧数据)，PNG不被支持。否定消息*/
#define J1939_ACCESS_DENIED_CONTROL_BYTE 2  /**< 拒绝访问，但是信息是被支持，暂时不能响应（需要再次发送请求）*/
#define J1939_CANNOT_RESPOND_CONTROL_BYTE 3 /**< 不能做出反应，有空但是接受的缓存不够，或则发送资源被占领，暂时不能响应（需要再次发送请求）*/

//TP协议的一些宏定义
#define J1939_PF_DT 235    /**< 协议传输---数据传输 PF*/
#define J1939_PF_TP_CM 236 /**< 协议传输---链接管理 PF*/

//TP的超时时间，单位（ms）
#define J1939_TP_Tr 200                  /**< 宏定义TP的超时时间*/
#define J1939_TP_Th 500                  /**< 宏定义TP的超时时间*/
#define J1939_TP_T1 750                  /**< 宏定义TP的超时时间*/
#define J1939_TP_T2 1250                 /**< 宏定义TP的超时时间*/
#define J1939_TP_T3 1250                 /**< 宏定义TP的超时时间*/
#define J1939_TP_T4 1050                 /**< 宏定义TP的超时时间*/
#define J1939_TP_TIMEOUT_NORMAL 0        /**< 未超时正常*/
#define J1939_TP_TIMEOUT_ABNORMAL 1      /**< 超时*/
#define J1939_RTS_CONTROL_BYTE 16        /**< TP.CM_RTS*/
#define J1939_CTS_CONTROL_BYTE 17        /**< TP.CM_CTS*/
#define J1939_EOMACK_CONTROL_BYTE 19     /**< 消息应答结束*/
#define J1939_BAM_CONTROL_BYTE 32        /**< 广播公告消息*/
#define J1939_CONNABORT_CONTROL_BYTE 255 /**< 连接中断控制字节（放弃连接）*/
#define J1939_RESERVED_BYTE 0xFF         /**< 变量的保留位的值*/

//与J1939网络层有关的定义
#define J1939_PGN2_REQ_ADDRESS_CLAIM 0x00
#define J1939_PGN1_REQ_ADDRESS_CLAIM 0xEA
#define J1939_PGN0_REQ_ADDRESS_CLAIM 0x00

#define J1939_PGN2_COMMANDED_ADDRESS 0x00
#define J1939_PGN1_COMMANDED_ADDRESS 0xFE /**< 命令地址消息*/
#define J1939_PGN0_COMMANDED_ADDRESS 0xD8 /**< 参考J1939-81 地址命令配置*/

#define J1939_PF_ADDRESS_CLAIMED 238
#define J1939_PF_CANNOT_CLAIM_ADDRESS 238
#define J1939_PF_PROPRIETARY_A 239 /**< 专用A*/
#define J1939_PF_PROPRIETARY_B 255 /**< 专用B*/

/**< 是否对TP协议的支持（是否支持长帧（大于8字节的数据）的发送与接受）*/
#define J1939_TP_RX_TX J1939_TRUE
/**< TP协议的支持的最大接受发送消息长度(最大可配置为1785)*/
#define J1939_TP_MAX_MESSAGE_LENGTH 240

#if J1939_TP_RX_TX
/**TP的状态描述枚举
*
*/
typedef enum
{
    J1939_TP_NULL,   /**< 长数据传输处于空闲，只有TP系统处于空闲，才能用处理下一个发送，和接受请求*/
    J1939_TP_RX,     /**< 长数据传输处于接收*/
    J1939_TP_TX,     /**< 长数据传输处于发送*/
    J1939_TP_OSBUSY, /**< 长数据传输处于繁忙，比如刚接受一整段长数据，但是CPU没来得处理，又一个长数据请求到来，为了数据不被覆盖，将状态设为本值*/
} J1939_TP_State;
/**TP的标志位结构体
*
* 本结构体记录了TP的状态，使用TP发送和接受的CAN硬件编号
*/
typedef struct
{
    J1939_TP_State state;    /**< TP的连接状态*/
    CAN_NODE TP_RX_CAN_NODE; /**< TP接受请求产生的 CAN硬件编号*/
    CAN_NODE TP_TX_CAN_NODE; /**< TP接受发送产生的 CAN硬件编号*/
} J1939_TP_Flags;
/**J1939消息对象的结构体
*
* 本结构体实现了 J1939的消息对象
*/
typedef struct
{
    j1939_uint32_t PGN;                              /**< J1939的消息对象的 PGN*/
    j1939_uint8_t data[J1939_TP_MAX_MESSAGE_LENGTH]; /**< J1939的消息对象的 数据*/
    j1939_uint16_t byte_count;                       /**< J1939的消息对象的 数据大小*/
    j1939_uint8_t SA;                                /**< J1939的消息对象的 目标地址（发送目的地  或  接受来源地）*/

} J1939_MESSAGE_T;
/**J1939消息对象的结构体
*
* 本结构体实现了 J1939的多帧消息对象
*/
typedef struct
{
    j1939_uint8_t *data;       /**< 缓存区指针*/
    j1939_uint16_t data_num;   /**< 缓存区大小*/
    j1939_uint8_t SA;          /**< J1939的消息对象的 数据 源地址*/
    j1939_uint16_t byte_count; /**< J1939的消息对象的 数据大小*/
    j1939_uint32_t PGN;        /**< J1939的消息对象的 PGN*/
} TP_RX_MESSAGE;
/**J1939_TP_Tx_Step枚举
*
* 实现了记录长帧（多帧）传输的TX 的步骤
*/
typedef enum
{
    J1939_TP_TX_WAIT,
    J1939_TP_TX_CM_START,
    J1939_TP_TX_CM_WAIT,
    J1939_TP_TX_DT,
    J1939_TP_WAIT_ACK,
    J1939_TP_TX_ERROR,
    J1939_TX_DONE,
} J1939_TP_Tx_Step; //协议的发送步骤
/**J1939_TRANSPORT_TX_INFO 结构体
*
* 实现了长帧传输中产生的临时数据，和一些传输交换数据
*/
typedef struct
{
    J1939_MESSAGE_T tp_tx_msg;         /**< J1939的消息对象*/
    j1939_uint16_t time;               /**< 时间*/
    j1939_uint8_t packet_offset_p;     /**< 数据包偏移指针*/
    j1939_uint8_t packets_total;       /**< 总共有多少个数据包*/
    j1939_uint8_t packets_request_num; /**< 请求发送的数据包数（接受方准备接受的数据包数）*/
    J1939_TP_Tx_Step state;            /**< 协议的发送步骤*/
} J1939_TRANSPORT_TX_INFO;
/**J1939_TP_Rx_Step枚举
*
* 实现了记录长帧（多帧）传输的RX 的步骤
*/
typedef enum
{
    J1939_TP_RX_WAIT,
    J1939_TP_RX_READ_DATA,
    J1939_TP_RX_DATA_WAIT,
    J1939_TP_RX_ERROR,
    J1939_RX_DONE,
} J1939_TP_Rx_Step; //协议的接收步骤

/**J1939_TRANSPORT_RX_INFO 结构体
*
* 实现了长帧传输中产生的临时数据，和一些传输交换数据
*/
typedef struct
{
    J1939_MESSAGE_T tp_rx_msg;    /**< J1939的消息对象*/
    j1939_uint8_t osbusy;         /**< 此位置1，代表系统繁忙，cpu需要处理其他的事物，直接拒绝一切的链接请求\n 如果正在接受中，此位置1，则会发出链接保持消息帧。*/
    j1939_uint16_t time;          /**< 时间*/
    j1939_uint8_t packets_total;  /**< 总共有多少个数据包*/
    j1939_uint8_t packets_ok_num; /**< 已经接受的数据包数*/
    J1939_TP_Rx_Step state;       /**< 协议的接受步骤*/
} J1939_TRANSPORT_RX_INFO;

#endif //J1939_TP_RX_TX

/**
* @note 实现Request_PGN 的响应
*/
struct Request_List
{
    j1939_uint8_t *data;
    j1939_uint16_t lenght;
    j1939_uint32_t PGN;
    CAN_NODE Can_Node;
    void (*update)();          /**< 在函数里需要对data更新，如果不用更新data赋值为J1939_NULL*/
    struct Request_List *next; /**< 链表末尾，需要一直保持J1939_NULL*/
};

// J1939 Data Structures
// J1939_MESSAGE_STRUCT旨在J1939消息块映射到设备的地址映射。 只有字段PDU格式不映射到设备寄存器。
// 结构应该简单地使用PDUFormat和忽视PDUFormat_Top。调整将立即接收和传输之前。
// 注:编译器创建结构从低一点的位置高一些位置，所以可能出现不匹配的设备寄存器。
#define J1939_MSG_LENGTH 9  //消息长度
#define J1939_DATA_LENGTH 8 //数据长度

/** J1939_MESSAGE_UNION 结构体
* 实现了J1939消息对象
*
*
*/
union J1939_MESSAGE_UNION
{
    /** j1939 的 ID 组成结构体
*
*/
    struct j1939_PID
    {
        /* First One Byte */
        j1939_uint8_t DataPage : 1;            /**< 数据页, Lowest Bit*/
        j1939_uint8_t Res : 1;                 /**< Res位EDP, always be zero*/
        j1939_uint8_t Priority : 3;            /**< 优先级*/
        j1939_uint8_t Reserve : 3;             /**< 空闲, First Three Bits*/

        j1939_uint8_t PDUFormat;               /**< PF*/
        j1939_uint8_t PDUSpecific;             /**< PS*/
        j1939_uint8_t SourceAddress;           /**< SA*/
        /* End Of 29 Bit J1939 Head*/

        j1939_uint8_t DataLength : 4;          /**< 数据长度*/
        j1939_uint8_t RTR : 4;                 /**< RTR位*/
        j1939_uint8_t Data[J1939_DATA_LENGTH]; /**< 数据*/
        j1939_uint32_t PGN : 24;               /**< 参数群编号*/
        j1939_uint32_t ReservePGN : 8;         /**< 空闲*/
    };
    struct j1939_PID Mxe;                                      /**< j1939 的 ID 组成结构体*/
    j1939_uint8_t Array[J1939_MSG_LENGTH + J1939_DATA_LENGTH]; /**< 联合体数组，方便快速处理结构体赋值*/
};

#define GroupExtension PDUSpecific
#define DestinationAddress PDUSpecific
/** 一个宏定义，具体变量名称作用命名
*
*/
typedef union J1939_MESSAGE_UNION J1939_MESSAGE;

union J1939_FLAGS_UNION
{
    struct
    {
        j1939_uint8_t TransmitMessagesdCover : 1; //发送数据时，J1939协议接受缓存有数据覆盖
        j1939_uint8_t ReceivedMessagesdCoverOrDroppedNode : 3;
        j1939_uint8_t ReceivedMessagesdCover : 1;  //接受数据时，J1939协议接受缓存有数据覆盖
        j1939_uint8_t ReceivedMessagesDropped : 1; //接受数据时，J1939协议接受缓存有数据溢出
    };
    j1939_uint8_t FlagVal;
};

typedef union J1939_FLAGS_UNION J1939_FLAG;

/********************************************API**************************************************************/

//初始化函数
extern void J1939_Initialization();
//CAN驱动收发中断入口
extern void J1939_ISR();
//心跳函数,定时被调用
extern void J1939_Poll();
//读取单帧消息
extern j1939_uint8_t J1939_Read_Message(J1939_MESSAGE *MsgPtr, CAN_NODE _Can_Node);
//发送单帧消息
extern j1939_uint8_t J1939_Send_Message(J1939_MESSAGE *MsgPtr, CAN_NODE _Can_Node);
//多帧（多组）消息发送函数  (RTS/CTS传输协议)
extern j1939_int8_t J1939_TP_TX_Message(j1939_uint32_t PGN, j1939_uint8_t DA, j1939_uint8_t *data, j1939_uint16_t data_num, CAN_NODE _Can_Node);
//多帧（多组）消息接受函数  (RTS/CTS传输协议)
extern j1939_int8_t J1939_TP_RX_Message(TP_RX_MESSAGE *msg, CAN_NODE _Can_Node);
//请求获去一个PGN
extern void J1939_Request_PGN(j1939_uint32_t pgn, j1939_uint8_t DA, CAN_NODE _Can_Node);
//创建一个PGN响应
extern void J1939_Create_Response(j1939_uint8_t data[], j1939_uint16_t dataLenght, j1939_uint32_t PGN, void (*dataUPFun)(), CAN_NODE _Can_Node);

#endif //__J1939_H
